/**
 * 请求/响应模式
 * 用于全局资源管理
 * 注意：响应过程不要太过耗时，否则将明显阻塞调用
 */

#ifndef REQREP_H
#define REQREP_H

#include <QObject>
#include <QMultiMap>
#include <QVariant>
#include <QMutex>
#include <functional>

#define REQREP_METHOD(method) # method
#define REQREP_REPLY [&](int subject, bool &ok) -> QVariant

class ReqRep : public QObject
{
    Q_OBJECT
public:

    typedef std::function<QVariant (int,bool&)> Reply;

    enum Subject {
        Subject_TestMethod,
        Subject_TestFunction,
        Subject_FlowId,
    };
    Q_ENUM(Subject)

    explicit ReqRep(QObject *parent = nullptr);
    ~ReqRep();
    static ReqRep &getInstance();

    void addResponder(int subject, QObject *responder, const char *replyMethod);
    ReqRep::Reply addResponder(int subject, Reply reply);

    void removeResponder(int subject, const QObject *responder);
    void removeResponder(const QObject *responder);
    void removeResponder(int subject, const Reply reply);
    void removeResponder(const Reply reply);

    QVariant request(int subject, bool *ok=nullptr);

    Q_INVOKABLE QVariant onTestRep(int subject, bool &ok);

private:
    QMutex mutex;
    Reply testReply;
    struct RepInfo {
        bool isMethod;
        QObject *responder;
        const char *method;
        Reply reply;

        RepInfo()
        {
            isMethod = true;
            responder = nullptr;
            method = nullptr;
            reply = nullptr;
        }

        RepInfo(QObject *responder, const char *method)
        {
            isMethod = true;
            this->responder = responder;
            this->method = method;
            reply = nullptr;
        }

        RepInfo(Reply reply)
        {
            isMethod = false;
            responder = nullptr;
            method = nullptr;
            this->reply = reply;
        }

        bool operator ==(const RepInfo &other)
        {
            if(this->isMethod != other.isMethod) {
                return false;
            }

            if(this->isMethod) {
                return this->responder == other.responder && this->method == other.method;
            } else {
                return &(this->reply) == &(other.reply);
            }
        }
    };

    QMultiMap<int, RepInfo> responders;
};

static inline QVariant ReqRep_request(int subject, bool *ok=nullptr)
{
    return ReqRep::getInstance().request(subject, ok);
}

#endif // REQREP_H
